package main

import (
	"fmt"
	"golang.org/x/crypto/ssh"
	"log"
	"net"
)

func passwordCallback(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
	if c.User() == "root" && string(pass) == "root" {
		return nil, nil
	}
	return nil, fmt.Errorf("用户名密码错误: 用户", c.User())
}

func createSshServer(address string, serverReady chan<- bool) {
	config := &ssh.ServerConfig{
		PasswordCallback: passwordCallback,
	}

	pkey, err := ssh.ParsePrivateKey([]byte(serverRsaPrivateKey))
	if err != nil {
		log.Fatal("SSHD私钥解析失败")
	}

	config.AddHostKey(pkey)

	listener, err := net.Listen("tcp", address)
	if err != nil {
		log.Fatal("SSHD无法监听端口")
	}

	serverReady <- true
	log.Println("SSHD已启动")

	// 开始处理客户端请求
	for {
		connection, err := listener.Accept()
		if err != nil {
			log.Println("SSHD错误的请求", err)
			continue
		}
		defer connection.Close()

		log.Println("SSHD新连接: ", connection.RemoteAddr())

		// 先读取握手请求
		_, chans, reqs, err := ssh.NewServerConn(connection, config)
		if err != nil {
			log.Println("SSHD获取Chanel失败")
			continue
		}

		// 握手请求忽略掉
		go ssh.DiscardRequests(reqs)

		//获取Channel
		channel := <-chans
		if channel.ChannelType() != "direct-tcpip" {
			channel.Reject(ssh.UnknownChannelType, "未知的Channel类型")
			continue
		}

		go handleChannel(channel)
	}
}

func handleChannel(channel ssh.NewChannel) {
	// 等候客户端请求
	fromConn, _, err := channel.Accept()
	if err != nil {
		log.Println("SSHD无效客户端请求")
		return
	}

	var data struct {
		Host       string
		Port       uint32
		OriginHost string
		OriginPort uint32
	}

	ssh.Unmarshal(channel.ExtraData(), &data)
	toAddr := fmt.Sprintf("%s:%d", data.Host, data.Port)
	fromAddr := fmt.Sprintf("%s:%d", data.OriginHost, data.OriginPort)

	log.Println("SSHD新隧道: ", fromAddr, "<=>", toAddr)

	//连接客户端请求的目的地
	toConn, err := net.Dial("tcp", toAddr)
	if err != nil {
		log.Println("SSHD连接远端失败: ", err)
	}

	//读写隧道数据直至断开
	disconnect := make(chan bool)
	go forwardRequest(fromConn, toConn, disconnect)
	go forwardRequest(toConn, fromConn, disconnect)

	<-disconnect
}
